#include <avr/pgmspace.h>
#include "common.h"

//All ASCII chars
#define FONT_6X8_BASE_H		0x0C
#define FONT_6X8_BASE_L		0x4200

//All ASCII chars
#define FONT_8X16_BASE_H		0x0F
#define FONT_8X16_BASE_L		0xCF00

//All ASCII chars
#define FONT_12X24_BASE_H		0x0F
#define FONT_12X24_BASE_L		0xD4F0

//Number: 0~9
#define FONT_16X32_BASE0_H	0x0C
#define FONT_16X32_BASE0_L	0x5BA0

//Chars: A~Z a~z : .
#define FONT_16X32_BASE1_H	0x0F
#define FONT_16X32_BASE1_L	0xE250

//GB2312
#define FONT_HZ_BASE0			0x0C0000
#define FONT_HZ_BASE1			0x0C69C0

//====================================================

void draw_6x8_char(char ch);
void draw_8x16_char(char ch);
void draw_12x24_char(char ch);
void draw_16x32_char(char ch);
void draw_hz(uint16_t hz);

byte g_cursor_X, g_cursor_Y;
typedef void (*t_draw_func)(char);
t_draw_func char_draw_func = draw_8x16_char;

bool g_invert;

void draw_clr_lines(uint8_t line_mask)
{
	uint8_t i=8, *p;

	for(i=0; i<8; i++) {
		if(line_mask & 1) {
			p = &oledBuffer[i*128];
			memset(p, 0, 128);
		}
		line_mask >>= 1;
	}
	g_cursor_X = 0;
	g_cursor_Y = 0;
}

void draw_clr_screen(void) 
{
    draw_clr_lines(0xff);
}

void draw_string(char* string, bool invert, byte x, byte y)
{
    uint8_t c;
	g_cursor_X = x;
	g_cursor_Y = y;
	g_invert = invert;
	
    while ((c=*string++) != 0x00) {
		if(c<0x80) {
			char_draw_func(c);
		} else {
			draw_hz((((uint8_t)c) << 8) + ((uint8_t)(*string++)));
		}
    }
}

void draw_end(void)
{
	oled_flush();
}

uint8_t * get_draw_pointer(uint16_t font_mem_span)
{
	uint8_t * pointer = oledBuffer + g_cursor_X + (g_cursor_Y/8)*128;

	if((pointer+font_mem_span) > &oledBuffer[FRAME_BUFFER_SIZE-1]) {
		pointer = oledBuffer;
	}
	
	return pointer;
}

void draw_set_font(uint8_t font_type)
{
	if(font_type == FONT_6X8) {
		char_draw_func = draw_6x8_char;
	} else if(font_type == FONT_12X24) {
		char_draw_func = draw_12x24_char;
	} else if(font_type == FONT_16X32) {
		char_draw_func = draw_16x32_char;
	} else {
		char_draw_func = draw_8x16_char;
	}
}

void move_cursor(byte w, byte h)
{
	g_cursor_X += w;
	if(g_cursor_X > (FRAME_WIDTH-w)) {
		g_cursor_X = 0;
		g_cursor_Y += h;
	}
	if(g_cursor_Y >= FRAME_HEIGHT) {
		g_cursor_Y = 0;
	}
}

void load_font_data(uint8_t addr_H, uint16_t addr_L, uint8_t *data_buff, uint16_t len)
{
	spi_read_data(addr_H, addr_L, data_buff, len);
	if(g_invert) {
		while(len--) {
			*data_buff = ~(*data_buff);
			data_buff++;
		}
	}
}

void draw_6x8_char(char ch)//6x8
{
	uint16_t offset;
	uint8_t *pointer;

	if(ch == '\n') {
		g_cursor_X = 0;
		g_cursor_Y += 8;
		return;
	}

	pointer = get_draw_pointer(6-1);
	offset = FONT_6X8_BASE_L + ((uint16_t)(ch - ' ') * 6);

	load_font_data(FONT_6X8_BASE_H, offset, pointer, 6);
	move_cursor(6, 8);
}

void draw_8x16_char(char ch)
{
	uint16_t offset;
	uint8_t *pointer;

	if(ch == '\n') {
		g_cursor_X = 0;
		g_cursor_Y += 16;
		return;
	}

	pointer = get_draw_pointer(128+8-1);
	offset = FONT_8X16_BASE_L + ((uint16_t)(ch - ' ') << 4);

	load_font_data(FONT_8X16_BASE_H, offset, pointer, 8);
	load_font_data(FONT_8X16_BASE_H, offset+8, pointer+128, 8);
	move_cursor(8, 16);
}

void draw_12x24_char(char ch)
{
	uint16_t offset;
	uint8_t *pointer;

	if(ch == '\n') {
		g_cursor_X = 0;
		g_cursor_Y += 24;
		return;
	}

	if(g_cursor_X > (FRAME_WIDTH-12)) {
		move_cursor(12, 24);
	}

	pointer = get_draw_pointer(128*2+12-1);
	offset = FONT_12X24_BASE_L + ((uint16_t)(ch - ' ') * 36);

	for(byte i=0; i<3; i++) {
		load_font_data(FONT_12X24_BASE_H, offset, pointer, 12);
		pointer += 128;
		offset+=12;
	}
	move_cursor(12, 24);
}

void draw_16x32_char(char ch)
{
	uint16_t offset, index;
	uint16_t base_l = FONT_16X32_BASE1_L;
	byte	 base_h = FONT_16X32_BASE1_H;
	uint8_t *pointer;

	if(ch == '\n') {
		g_cursor_X = 0;
		g_cursor_Y += 32;
		return;
	}

	if(g_cursor_X > (FRAME_WIDTH-16)) {
		move_cursor(16, 32);
	}

	pointer = get_draw_pointer(128*3+16-1);

	if((ch >= '0') && (ch <= '9')) {
		index = ch - '0';
		base_l = FONT_16X32_BASE0_L;
		base_h = FONT_16X32_BASE0_H;
	} else if((ch >= 'A') && (ch <= 'Z')) {
		index = ch - 'A';
	} else if((ch >= 'a') && (ch <= 'z')) {
		index = ch - 'a' + 26;
	} else {
		if(ch == ':') {
			index = 52;
		} else if(ch == '.') {
			index = 53;
		} else {
			return;
		}
	}

	offset = base_l + (index * 64);

	for(byte i=0; i<4; i++) {
		load_font_data(base_h, offset, pointer, 16);
		pointer += 128;
		offset+=16;
	}
	move_cursor(16, 32);
}

void draw_hz(uint16_t hz)
{
	uint32_t fontaddr;
	uint8_t *pointer;
	uint32_t H = (hz>>8)&0xff;
	uint32_t L = hz&0xff;
	uint16_t offset_l;
	uint8_t offset_H;

	if(g_cursor_X > (FRAME_WIDTH-16)) {
		move_cursor(16, 16);
	}

	pointer = get_draw_pointer(128+16-1);

	if(H < 0xB0) {
		fontaddr = ((H - 0xA1)*94 + (L-0xA1))*32 + FONT_HZ_BASE0;
	} else {
		fontaddr = ((H - 0xB0)*94 + (L-0xA1))*32 + FONT_HZ_BASE1;
	}

	offset_l = fontaddr & 0xffff;
	offset_H = (fontaddr >> 16) & 0xff;

	load_font_data(offset_H, offset_l, pointer, 16);
	load_font_data(offset_H, offset_l+16, pointer+128, 16);

	move_cursor(16, 16);
}
